package com.example.cephdemo.demos.service;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.example.cephdemo.demos.base.MultipartUploadCreate;
import com.example.cephdemo.demos.base.ResultCode;
import com.example.cephdemo.demos.exception.BusinessException;
import com.example.cephdemo.demos.request.CompleteMultipartUploadRequest;
import com.example.cephdemo.demos.request.MultipartUploadCreateRequest;
import com.example.cephdemo.demos.response.FileUploadResponse;
import com.example.cephdemo.demos.response.MultipartUploadCreateResponse;
import com.example.cephdemo.demos.utils.MinioHelper;
import io.minio.CreateMultipartUploadResponse;
import io.minio.ListPartsResponse;
import io.minio.ObjectWriteResponse;
import io.minio.errors.InsufficientDataException;
import io.minio.errors.ServerException;
import io.minio.messages.Part;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

@Service
@RequiredArgsConstructor
@Slf4j
public class FileService {
    private final MinioHelper minioHelper;


    /**
     * 普通上传
     * @param file
     * @return
     */
    public FileUploadResponse upload(MultipartFile file, String bucket) {
        Assert.notNull(file, "文件不能为空");
        log.info("start file upload");

        //文件上传
        try {
            return minioHelper.uploadFile(file, bucket);
        } catch (IOException e) {
            log.error("file upload error.", e);
            throw BusinessException.newBusinessException(ResultCode.FILE_IO_ERROR.getCode());
        } catch (ServerException e) {
            log.error("minio server error.", e);
            throw BusinessException.newBusinessException(ResultCode.MINIO_SERVER_ERROR.getCode());
        } catch (InsufficientDataException e) {
            log.error("insufficient data throw exception", e);
            throw BusinessException.newBusinessException(ResultCode.MINIO_INSUFFICIENT_DATA.getCode());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw BusinessException.newBusinessException(ResultCode.KNOWN_ERROR.getCode());
        }
    }


    /**
     * 创建分片上传
     * @param createRequest
     * @return
     */
    public MultipartUploadCreateResponse createMultipartUpload(MultipartUploadCreateRequest createRequest) {
        log.info("创建分片上传开始, createRequest: [{}]", createRequest);
        MultipartUploadCreateResponse response = new MultipartUploadCreateResponse();
        response.setChunks(new LinkedList<>());
        final MultipartUploadCreate uploadCreate = MultipartUploadCreate.builder()
                .bucketName(createRequest.getBucket())
                .objectName(createRequest.getFileName())
                .build();
        final CreateMultipartUploadResponse uploadId = minioHelper.uploadId(uploadCreate);
        uploadCreate.setUploadId(uploadId.result().uploadId());
        response.setUploadId(uploadCreate.getUploadId());
        Map<String, String> reqParams = new HashMap<>();
        reqParams.put("uploadId", uploadId.result().uploadId());
        for (int i = 1; i < createRequest.getChunkSize(); i++) {
            reqParams.put("partNumber", String.valueOf(i));
            String presignedObjectUrl = minioHelper.getPresignedObjectUrl(uploadCreate.getBucketName(), uploadCreate.getObjectName(), reqParams);
            MultipartUploadCreateResponse.UploadCreateItem item = new MultipartUploadCreateResponse.UploadCreateItem();
            item.setPartNumber(i);
            item.setUploadUrl(presignedObjectUrl);
            response.getChunks().add(item);
        }
        log.info("创建分片上传结束, createRequest: [{}]", createRequest);
        return response;
    }

    /**
     * 分片合并
     * @param uploadRequest
     */
    public FileUploadResponse completeMultipartUpload(CompleteMultipartUploadRequest uploadRequest) {
        log.info("文件合并开始, uploadRequest: [{}]", uploadRequest);
        Part[] parts = new Part[10000];
        int partNumber = 1;
            final ListPartsResponse listMultipart = minioHelper.listMultipart(MultipartUploadCreate.builder()
                    .bucketName(uploadRequest.getBucket())
                    .objectName(uploadRequest.getFileName())
                    .maxParts(uploadRequest.getChunkSize() + 10)
                    .uploadId(uploadRequest.getUploadId())
                    .partNumberMarker(0)
                    .build());
            for (Part part : listMultipart.result().partList()) {
                parts[partNumber - 1] = new Part(partNumber, part.etag());
                partNumber++;
            }
        StringBuilder sb = new StringBuilder();
        Part[] partsList = listMultipart.result().partList().toArray(new Part[]{});
        for (Part p : partsList) {
            sb.append(StrUtil.format("etag: {}, partSize: {}, partNumber: {}", p.etag(), p.partSize(), p.partNumber()));
        }
        log.info("bucket: {}, uploadId: {}, filename: {}", "test", uploadRequest.getUploadId(), uploadRequest.getFileName());
        log.info("parts: {}", sb);
            minioHelper.completeMultipartUpload(MultipartUploadCreate.builder()
                    .bucketName(uploadRequest.getBucket())
                    .uploadId(uploadRequest.getUploadId())
                    .objectName(uploadRequest.getFileName())
                    .parts(partsList)
                    .build());

            return FileUploadResponse.builder()
                    .url(minioHelper.minioProperties.getEndpoint() + "/" + uploadRequest.getBucket() + "/" + uploadRequest.getFileName())
                    .build();
    }


    public void remove(String fileName, String bucket) {
        if (!StringUtils.hasText(fileName)) return;
        log.info("删除文件开始, fileName: [{}]",fileName);
        try {
            minioHelper.removeFile(fileName, bucket);
        } catch (Exception e) {
            log.error("删除文件失败", e);
        }
        log.info("删除文件结束, fileName: [{}]",fileName);
    }
}
